package GraphTest;
import bbGraphs.Graph;
import bbGraphs.Node;
import bbGraphs.Pair;

import java.util.*;

class rightSideComparatorDouble implements Comparator< Pair< Integer, Double > >
{
	public int compare( Pair< Integer, Double > _pair1, Pair< Integer, Double > _pair2 )
	{
		if ( _pair1.getRight() < _pair2.getRight() ) 
			return -1;
		if ( _pair1.getRight() > _pair2.getRight() ) 
			return 1;
		return 0;
	}
}

class rightSideComparatorInt implements Comparator< Pair< Integer, Integer > >
{
	public int compare( Pair< Integer, Integer > _pair1, Pair< Integer, Integer > _pair2 )
	{
		if ( _pair1.getRight() < _pair2.getRight() ) 
			return -1;
		if ( _pair1.getRight() > _pair2.getRight() ) 
			return 1;
		return 0;
	}
}

public class GraphTester 
{
	Graph m_baseGraph;
	int m_graphInstances;
	int m_initialRedSelection;
	float m_activeEdgeProb;
	String m_redSelectionAlg;
	
	public static String DEGREE_CENTRALITY = "DEGREE_CENTRALITY";
	public static String CLOSENESS_CENTRALITY = "CLOSENESS_CENTRALITY";
	
	public GraphTester( Graph _baseGraph,
						float _activeEdgeProb,
			            int _initialRedSelection,
			            String _redSelectionAlg,
			            int _graphInstances )
	{
		m_activeEdgeProb = _activeEdgeProb;
		m_graphInstances = _graphInstances;
		m_baseGraph = _baseGraph;
		m_initialRedSelection = _initialRedSelection;
		m_redSelectionAlg = _redSelectionAlg;
	}
	
	
	
	public int runTests()
	{
		// First, let's make an array with a bunch of graph instances. 
		ArrayList< Graph > graphInstances = new ArrayList< Graph >();
		for( int index = 0; index < m_graphInstances; ++m_graphInstances )
		{
			try 
			{
				Graph tempGraph = (Graph)m_baseGraph.clone();
				Graph.initializeActiveEdges( tempGraph, m_activeEdgeProb );
				graphInstances.add( tempGraph );
			} catch (CloneNotSupportedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		// Let's initialize values to select red nodes
		HashMap< Integer, Double > nodeValMap = new HashMap< Integer, Double >();
		for( int nodeNum : m_baseGraph.m_edgeMap.keySet() )
		{
			nodeValMap.put( nodeNum, 0.0 );
		}

		// Let's add up the value for all graph instances
		for( Graph graphInstance: graphInstances )
		{
			HashMap< Integer, Double > selectionResults = this.makeFirstSelection( graphInstance, m_redSelectionAlg );
			for( int nodeNum : m_baseGraph.m_edgeMap.keySet() )
			{
				nodeValMap.put( nodeNum, ( nodeValMap.get( nodeNum ) + selectionResults.get( nodeNum ) ) );
			}
		}

		// Now we need to find which node is deemed the most "valuable"
		ArrayList< Pair< Integer, Double > > nodeList = new ArrayList< Pair< Integer, Double > >();

		for( int nodeNum : nodeValMap.keySet() )
		{
			nodeList.add( new Pair< Integer, Double >( nodeNum, nodeValMap.get( nodeNum ) ) );
		}

		Collections.sort( nodeList, new rightSideComparatorDouble() );
		
		this.setupFirstSelections( graphInstances, nodeList, m_initialRedSelection, Node.COLOR.RED );
		
		// Ok, time for the blue node stuff
		int blueNodeDiff = 0;
		while( blueNodeDiff <= 0 )
		{
			//Shahrzad: Shouldn't the second value in blueNodeValMap be double? It shows the difference between the expected number of Blue nodes and expected number of red nodes
			HashMap< Integer, Integer > blueNodeValMap = new HashMap< Integer, Integer >();
			for( int nodeNum : m_baseGraph.m_edgeMap.keySet() )
			{
				blueNodeValMap.put( nodeNum, 0 );
			}
			
			// For each of the graph instances, we need to find the diff in red and blue nodes after propagation
			for( Graph graphInstance: graphInstances )
			{
				HashMap< Integer, Integer > selectionResults = this.makeSecondSelection( graphInstance, Node.COLOR.BLUE );
				for( int nodeNum : m_baseGraph.m_edgeMap.keySet() )
				{
					blueNodeValMap.put( nodeNum, ( blueNodeValMap.get( nodeNum ) + selectionResults.get( nodeNum ) ) );
				}
			}
			
			// Now we have to sort the data
			ArrayList< Pair< Integer, Integer > > blueNodeList = new ArrayList< Pair< Integer, Integer > >();
			for( int nodeNum : nodeValMap.keySet() )
			{
				blueNodeList.add( new Pair< Integer, Integer >( nodeNum, blueNodeValMap.get( nodeNum ) ) );
			}
			Collections.sort( blueNodeList, new rightSideComparatorInt() );
			
			blueNodeDiff = makeSecondSelections( graphInstances, blueNodeList, Node.COLOR.BLUE );
		}
		
		//Shahrzad: Shouldn't it return the number of blue nodes, which is equal to the number of iterations in while loop?
		return blueNodeDiff;
	}
	
	private int makeSecondSelections( ArrayList<Graph> _graphInstances, ArrayList< Pair< Integer, Integer > > _nodeList, Node.COLOR _nodeColor ) 
	{
		
		Integer nodeID = _nodeList.get( 0 ).getLeft();
		for( Graph graphInstance: _graphInstances )
		{
			graphInstance.setNodeColor( nodeID, _nodeColor );
		}
		return _nodeList.get( 0 ).getRight();
	}



	private HashMap< Integer, Double > makeFirstSelection( Graph _graphInstance, String _selectionAlg )
	{
		HashMap< Integer, Double > selectionAlgResults = new HashMap< Integer, Double >();
		if( _selectionAlg == CLOSENESS_CENTRALITY )
		{
			selectionAlgResults = Graph.degreeCentralityList( _graphInstance );
		}
		else if( _selectionAlg == DEGREE_CENTRALITY )
		{
			selectionAlgResults = Graph.closenessCentralityList( _graphInstance );
		}
		else
		{
			System.out.println( "Cannot recognize selection algorithm: " + _selectionAlg );
		}
		
		return selectionAlgResults;
	}
	
	private void setupFirstSelections( ArrayList< Graph > _graphInstances, 
                                       ArrayList< Pair< Integer, Double > > _nodeList, 
                                       int _numSelected, 
                                       Node.COLOR _nodeColor )
	{
		for( int nodeNum = 0; nodeNum < _numSelected; ++nodeNum )
		{
			for( Graph graphInst: _graphInstances )
			{
				graphInst.setNodeColor( _nodeList.get( nodeNum ).getLeft(), _nodeColor );
			}
		}
	}
	
	private HashMap< Integer, Integer > makeSecondSelection( Graph _graphInstance, Node.COLOR _nodeColor )
	{
		HashMap< Integer, Integer > selectionResults = new HashMap< Integer, Integer >();
		int propDiff;
		
		for( int nodeIndex = 0; nodeIndex < _graphInstance.m_nodeMap.size(); ++nodeIndex )
		{
			if( _graphInstance.getNodeColor( nodeIndex ) != Node.COLOR.WHITE )
				continue;
			
			_graphInstance.setNodeColor( nodeIndex , _nodeColor );
			propDiff = Graph.propagate( _graphInstance );
			selectionResults.put( nodeIndex, propDiff );
			_graphInstance.resetNodeColor( nodeIndex );
		}
		
		return selectionResults;
	}
	
	

} /* End of class def */
